import { parseColorText } from '../../../common/color/color';
import { BaseComp } from '../../../common/component/comp';
import { calc, cubicOut } from '../../../common/animation';

import { z, layouts, color } from './layout';
import { Candidate } from './lottery';
import { fetchAvatarUrl, waitForFetchingAvatar } from '../utils';

/**
 * 移到屏幕外直接调用 this.parent.onSeatOutside(this)
 */
class Seat extends BaseComp {
  spBorder = null;
  spBorderLight = null;
  spBorderDark = null;
  spBorderTint = null;
  
  spUserName = null;
  spIconBox = null;
  spAvatar = null;

  spList = [];

  showing = false;
  uid = 0;

  /** 在发弹幕阶段时, 确定 seat 在什么时候移到哪里 */
  fromX = 0; toX = 0; fromY = 0; toY = 0; fromTime = 0;
  
  /** 正在进行刚加入时播放的动画 */
  comeTime = 0;
  selected = false;

  constructor(args) {
    super(args.parent);
    this.zrender = args.zrender;
    this.zr = args.zr;
    this.yLimit = args.yLimit;

    this.layout = layouts.seats;
  }

  init() {
    const [x, y] = this.absolutePos();
    this.initBorder(x, y);
    this.initTitle(x, y);
    this.initBack(x, y);

    if (!this.showing) {
      this.hide();
    }
  }

  initBorder(x, y) {
    const { zr, zrender, layout } = this;
    const blLayout = layout.borderLight;
    const bdLayout = layout.borderDark;

    // 内圈, light
    zr.add(this.spBorder = new zrender.Polygon({
      position : [x + blLayout.x, y + blLayout.y],
      shape : {
        points: [
          [blLayout.offset, 0],
          [blLayout.width - blLayout.offset, 0],
          [blLayout.width, blLayout.height / 2],
          [blLayout.width - blLayout.offset, blLayout.height],
          [blLayout.offset, blLayout.height],
          [0, blLayout.height / 2],
        ]
      },
      style: {
        fill: '#0000',
        stroke: color.dark1,
        lineWidth: 4,
      },
      z: z.seat.border,
    }));
    zr.add(this.spBorderLight = new zrender.Polygon({
      position : [x + blLayout.x, y + blLayout.y],
      shape : {
        points: [
          [blLayout.offset, 0],
          [blLayout.width - blLayout.offset, 0],
          [blLayout.width, blLayout.height / 2],
          [blLayout.width - blLayout.offset, blLayout.height],
          [blLayout.offset, blLayout.height],
          [0, blLayout.height / 2],
        ]
      },
      style: {
        fill: '#0000',
        stroke: color.light1,
        lineWidth: 2,
      },
      z: z.seat.borderLight,
    }));
    
    // 外圈, dark
    zr.add(this.spBorderDark = new zrender.Polygon({
      position : [x + bdLayout.x, y + bdLayout.y],
      shape : {
        points: [
          [bdLayout.offset, 0],
          [bdLayout.width - bdLayout.offset, 0],
          [bdLayout.width, bdLayout.height / 2],
          [bdLayout.width - bdLayout.offset, bdLayout.height],
          [bdLayout.offset, bdLayout.height],
          [0, bdLayout.height / 2],
        ]
      },
      style: {
        fill: '#0000',
        stroke: color.dark2,
        lineWidth: 2,
      },
      z: z.seat.borderDark,
    }));
    zr.add(this.spBorderTint = new zrender.Line({
      position : [x + bdLayout.x, y + bdLayout.y],
      shape : {
        x1: bdLayout.width / 2 - bdLayout.tintWidth / 2 + 6,
        x2: bdLayout.width / 2 + bdLayout.tintWidth / 2 + 6,
        y1: 0,
        y2: 0,
      },
      style: {
        fill: '#0000',
        stroke: color.tint,
        lineWidth: 2,
      },
      z: z.seat.borderTint,
    }));

    this.spList.push(this.spBorder, this.spBorderLight, this.spBorderDark, this.spBorderTint);
  }

  initTitle(x, y) {
    const { zr, zrender, layout } = this;
    zr.add(this.spUserName = new zrender.Text({
      position : [x + layout.userName.x, y + layout.userName.y],
      style : {
        text : '',
        fontSize : layout.userName.font,
        fontWeight: 'bold',
        textFill: '#FFF',
        textAlign: 'left',
        textVerticalAlign: 'top'
      },
      z : z.seat.uname
    }));
    this.spList.push(this.spUserName);
    //spIconBox
    zr.add(this.spIconBox = new zrender.Polygon({
      position : [x + layout.iconBox.x, y + layout.iconBox.y],
      shape : {
        points: [
          [layout.iconBox.width / 2, 0],
          [layout.iconBox.width, layout.iconBox.height / 2],
          [layout.iconBox.width / 2, layout.iconBox.height],
          [0, layout.iconBox.height / 2],
        ]
      },
      style: {
        fill: '#0000',
        stroke: color.tint,
        lineWidth: 4,
      },
      z: z.seat.iconBox,
    }));
    this.spList.push(this.spIconBox);
  }

  initBack(x, y) {
    // spAvatar: 等到知道是谁才会创建出来
  }

  createImage(uid) {
    const { zr, zrender, layout } = this;

    /** @type {Avatar} */
    this.avatar = createAvatar(uid);
    this.avatar.getPromise().then(canvas => {
      if (uid != this.uid) {
        return;
      }

      const [x, y] = this.absolutePos();
      this.removeImage();
      const spAvatar = this.spAvatar = new zrender.Image({
        style: {
          image: canvas,
          opacity: 0.5,
        },
        position: [x + layout.avatar.x, y + layout.avatar.y],
        z: z.seat.avatar
      });
      if (!this.showing) spAvatar.hide();
      zr.add(spAvatar);
    });
  }

  removeImage() {
    const { zr } = this;
    if (this.spAvatar != null) {
      zr.remove(this.spAvatar);
    }
    this.avatar && this.avatar.cancel();
  }

  show() {
    this.showing = true;
    this.paint();
    this.spList.forEach(sp => sp && sp.show());
    this.spAvatar && this.spAvatar.show();
    this.dropPrimary();
  }

  hide() {
    this.showing = false;
    this.spList.forEach(sp => sp && sp.hide());
    this.spAvatar && this.spAvatar.hide();
    this.comeTime = 0;
    this.fromTime = 0;
    this.avatar && this.avatar.cancel();
  }

  /**
   * @param {Candidate} candidate 
   */
  load(candidate) {
    this.spUserName.attr({style: {text: candidate.uname}});
    this.comeTime = 0;
    this.dropPrimary();
    this.uid = candidate.uid;
    this.uid && this.createImage(this.uid);
    // 所有刚加入时放的动画取消 TODO

  }

  come(x, y) {
    this.comeTime = now();
    this.x = x;
    this.y = y;
    this.dropPrimary();
  }

  /**
   * 设置动画, 移动的动画是 500 ms.
   * @param {number} toX 
   * @param {number} toY 
   */
  moveSlowly(toX, toY) {
    this.fromX = this.x;
    this.fromY = this.y;
    this.toX = toX;
    this.toY = toY;
    this.fromTime = now();
    this.dropPrimary();
  }

  tick() {
    if (this.comeTime != 0) {
      this.makeDirty();
      this.comeTime = 0;
    }
    if (this.fromTime != 0) {
      let delta = (now() - this.fromTime) / 500;
      delta = delta > 1 ? 1 : delta;
      this.x = calc(this.fromX, this.toX, delta, cubicOut);
      this.y = calc(this.fromY, this.toY, delta, cubicOut);
      if (delta == 1) {
        this.fromTime = 0;
      }
    }

    if (this.y < this.yLimit) {
      this.parent.onSeatOutside(this);
    }
  }

  paint() {
    if (!this.showing) {
      return;
    }

    const [x, y] = this.absolutePos();
    const { layout } = this;
    this.spBorder.attr({position: [x + layout.borderLight.x, y + layout.borderLight.y]});
    this.spBorderLight.attr({position: [x + layout.borderLight.x, y + layout.borderLight.y]});
    this.spBorderDark.attr({position: [x + layout.borderDark.x, y + layout.borderDark.y]});
    this.spBorderTint.attr({position: [x + layout.borderDark.x, y + layout.borderDark.y]});
    this.spUserName.attr({position: [x + layout.userName.x, y + layout.userName.y]});
    this.spIconBox.attr({position: [x + layout.iconBox.x, y + layout.iconBox.y]});
    this.spAvatar && this.spAvatar.attr({position: [x + layout.avatar.x, y + layout.avatar.y]});
  }

  makeSecond() {
    this.selected = true;
    // 它被选中了, 好开心啊
    let c = parseColorText(color.dark1);
    c.a = 128;

    this.spBorder.attr({style: {
      fill: c.toHexRGBA(),
      blend: 'lighten'
    }});
  }

  makePrimary() {
    this.selected = true;
    // 它被选中了, 好开心啊
    let c = parseColorText(color.tint);
    c.a = 128;

    this.spBorder.attr({style: {
      fill: c.toHexRGBA(),
      blend: 'lighten'
    }});
  }

  dropPrimary() {
    if (!this.selected) return;

    this.spBorder.attr({style: {
      fill: '#0000',
      blend: 'source-over',
    }});

    this.selected = false;
  }
}

// uid - avatar : cache
const avatarMap = {};

function createAvatar(uid) {
  if (avatarMap[uid]) {
    return avatarMap[uid];
  }
  return avatarMap[uid] = new Avatar(uid);
}

class Avatar {
  /**
   * @param {number} uid 
   */
  constructor(uid) {
    this.uid = uid;
    this.path = fetchAvatarUrl(uid);
    this.ok = false;
    this.canvas = document.createElement('canvas');
    this.width = this.canvas.width = 128;
    this.height = this.canvas.height = 32;
    this.fail = false;

    const promise = waitForFetchingAvatar(uid);
    this.imagePromise = promise.promise;
    this.cancalFunc = promise.cancel;
  }

  getPromise() {
    if (this.ok) {
      return Promise.resolve(this.canvas);
    }
    if (this.fail) {
      this.fail = false;
      const promise = waitForFetchingAvatar(this.uid);
      this.imagePromise = promise.promise;
      this.cancalFunc = promise.cancel;
    }
    return this.imagePromise.then(image => {
      this.handleCanvas(image);
      return this.canvas;
    }).catch(e => {
      console.error(e);
      this.fail = true;
      return this.canvas;
    });
  }

  handleCanvas(image) {
    const {width, height} = this;
    const ctx2d = this.canvas.getContext("2d");
    ctx2d.drawImage(image, 0, height, width, height, 0, 0, width, height);
    const imageData = ctx2d.getImageData(0, 0, width, height);
    const data = imageData.data;
    // 左边渐变效果, x 0~32
    for (let y = 0; y < height; y++) {
      let beginX = (1 - y / height) * 14;
      let endX = beginX + 32;
      for (let x = 0; x < endX; x++) {
        let alpha = x <= beginX ? 0 : (x - beginX) / 32;
        let index = (y * width + x) * 4 + 3; // 改的都是 alpha 通道
        data[index] = Math.floor(alpha * data[index]);
      }
    }
    ctx2d.putImageData(imageData, 0, 0);
    // 右边裁剪
    const offsetX = layouts.seats.borderLight.offset;
    ctx2d.lineWidth = 0;
    ctx2d.moveTo(width - offsetX, 0);
    ctx2d.lineTo(width, 0);
    ctx2d.lineTo(width, height);
    ctx2d.lineTo(width - offsetX, height);
    ctx2d.lineTo(width, height / 2);
    ctx2d.closePath();
    ctx2d.globalCompositeOperation = 'destination-out';
    ctx2d.fill();

    this.ok = true;
  }

  cancel() {
    if (this.ok) {
      return;
    }
    this.cancalFunc();
  }
}

function now() {
  return new Date().getTime();
}

export {
  Seat
};
